perm filename TCPPZ.MAC[IP,SYS] blob
sn#680228 filedate 1982-10-14 generic text, type T, neo UTF8
;CWL:<403-TCP>TCPPZ.MAC.40303 6-May-82 17:45:57, Edit by CLYNN
; Make packetizer combine user buffers into single packet
; Don't always fill window; EMTPKT uses time passed in T1
;<403-TCP>TCPPZ.MAC.40301 29-Jan-82 15:07:12, Edit by CLYNN
; Updated for TCP release 3
;[BBND]<401-TCP>TCPPZ.MAC.156, 13-Oct-81 17:45:00, Ed: CLYNN
; Fix: TVT LINBLK=-1 and testing PPROG in possibly vanished packet
;[BBNF]<401-TCP>TCPPZ.MAC.155, 14-Aug-81 14:03:00, Ed: CLYNN
; Fix: Call to LCKTTY with TVTL=0 after PKTIZ1, Call to TVTOSP with
; LINBLK=-1 after PKZ23A
;[BBNF]<401-TCP>TCPPZ.MAC.154, 10-Jul-81 14:13:00, Ed: CLYNN
; Fix: No room in PKTZ9A, use T3 at PKTZ18, use TPKT for TCP checksum
; in ABTNTC, no room in PZINI
;<401-TCP>TCPPZ.MAC.153, 4-Apr-81 17:25:46, Edit by TAPPAN
; merged with multiple net stuff
SEARCH INPAR,TCPPAR,PROLOG
TTITLE TCPPZ
SUBTTL TCP PACKETIZER, WILLIAM W. PLUMMER, 7JAN77
SWAPCD
COMMENT !
The PACKETIZER is called with TCB setup to point at a
(locked) connection block. It attempts to form packets
from data in any buffers which are queued from the
user SENDs. If the "force packet" bit is on, the
PACKETIZER will always generate a packet containing
an ACK, even if there is no data to be sent. Packetizing
continues until no more is available from user buffers or
until the send window has been filled.
In the case of virtual terminals (TVTs) output is
stored in TTY buffers and TVTNOF is set to cause a
scan by OPSCAN which forces a packet on TCBs which
are TVTs. PZ runs with BFR set to 0 and BFRCNT
set to infinity in this case since it is not known
how much output is waiting to go and since the buffers
are non-standard format.
* PKTIZE ... 3 ...... Construct packets for a connection
SETISN ... 12 ...... Set initial window and send sequence
SNDSYN ... 12 ...... Send a SYN
SNDDAT ... 13 ...... Send data
SNDFIN ... 15 ...... Send a FIN
* EMTPKT ... 16 ...... Emit a packet into the network
ABTNTC ... 18 ...... Say this end of connection was abandonned
* SCRCLS ... 19 ...... Send a Secure Close to BCR
* FRCPKT ... 20 ...... Cause packetizer to emit a packet
* ENCPKT ... 21 ...... Wait a while then force a packet
* DLAYPZ ... 21 ...... Ask background to run packetizer later
* FLSSBF ... 22 ...... Flush SEND buffers
* PZINI .... 23 ...... Initialize the PZ process block
!
;Packetizer
;TCB/ (Extended) Pointer to connction block
;
; CALL PKTIZE
;Ret+1: Always
PKTIZE::LOCAL <BUFCNT,XFRCNT,WNDSPC,LINBLK>
PUSH P,PKT
PUSH P,TPKT
PUSH P,BFR
SETO LINBLK, ; Assume not TVT (abort case)
; User did an ABORT for this connection or a RESET, CLZFF, LOGOUT etc.
; If a foreign address is known and we're not NOT.NOT a "non-existant"
; TCB error is sent to it so it can know that the connection is gone
; on this end.
JE TSABT,(TCB),PKTIZ0 ; User requested ABORT?
LOAD T1,TRSYN,(TCB) ; Get state of Recv and
LOAD T2,TSSYN,(TCB) ; Send synchronization
CAIN T1,NOTSYN
CAIE T2,NOTSYN
CAIN T1,SYNABL ; If we know the foreign address,
CAIA
CALL ABTNTC ; Send a courtesy error pkt to other end
MOVX T1,ELP+↑D7 ; "No such connection"
CALL ABTCON ; Set to NOTSYN, flush buffers, queues
SETZRO <TSUOP,TSFP>,(TCB) ; Fake user CLOSE, Clear Force Packet request
CALL USRABD ; Tell user that ABORT is done.
JRST PKTIZX
PKTIZ0:
; If packet is being encouraged, set Force packet bit to get it done.
JE TSEP,(TCB),PKTZ00
SETONE TSFP,(TCB)
PKTZ00:
; If a SYN is sent when the connection is first used,
; it should have a sequence number gotten from the "Initial
; Sequence Number" curve (a function of the clock).
;PKTIZ1:
LOAD T1,TSSYN,(TCB) ; Get send state
CAIN T1,SYNABL ; SYNCABLE?
CALL SETISN ; Yes. Set initial sequence number
; If this is a TVT, TSFP will be on but there will be no buffer.
; Do the special things for this case.
JE TTVT,(TCB),PKTZ1D ; Jump if not a TVT
MOVX BFRCNT,177700 ; Maybe lots of output to handle
LOAD T2,TVTL,(TCB) ; Get the line number
JUMPE T2,PKTZ1D ; None if no TVT assigned (not synced)
CALL LCKTTY ; Lock TTY, trm blk address to T2 & NOINT
JUMPLE T2,PKTZ1C ; Can't.
MOVEM T2,LINBLK ; Save the address
CALL TTSOBE ; CFOBF might have happened since OPSCAN
JRST PKTZ1E ; Chrs available. (Zero if ↑S)
MOVE T2,LINBLK ; Restore line blk address
PKTZ1C: CALL ULKTTY ; Decrease the lock count & OKINT
PKTZ1D: SETO LINBLK, ; No terminal block to unlock later
; Top of main non-TVT loop
;Try to find a user buffer to send data from. This could the the
; "send current buffer" which is left from a previous call or a
; buffer queued from user SEND. If there is no buffer, BFR is set
; to 0 as is the byte count.
PKTIZ1: LOAD BFR,TSCB,(TCB) ; Get current send buffer if any
JUMPN BFR,PKTIZ3 ; Got one. Go set count.
LOAD T1,QNEXT,<+TCBSBQ(TCB)> ; Get next thing on send buf Q
CAIN T1,TCBSBQ(TCB) ; Next points at header ...
JRST PKTZ1E ; means empty. No buffer.
SETSEC T1,INTSEC ; Make extended address
CALL DQ ; Dequeue the buffer
SKIPA BFR,T1 ; And setup the standard pointer.
PKTZ1E: MOVX BFR,0 ; 0 means no buffer
STOR BFR,TSCB,(TCB) ; Remember as current buffer
; Top of main TVT loop
; If there is a current buffer, set BUFCNT from the sum of the buffers.
; If no current buffer, try for a TVT. If neither, set BUFCNT to 0.
PKTIZ2: JUMPN BFR,PKTIZ3 ; Jump if we have a buffer
MOVX BUFCNT,0 ; Count if not a TVT
JUMPL LINBLK,PKTIZ4 ; Jump if not TVT, or TVT w/ empty output buf
MOVE T2,LINBLK ; Arg for TVTOSP
CALL TVTOSP ; Find out how many chrs to be sent
MOVE BUFCNT,T1 ; May be zero
STOR T1,TSBYT,(TCB) ; Keep TSBYT up to date too
JRST PKTIZ4
PKTIZ3: SETSEC BFR,INTSEC ; Make extended address
LOAD BUFCNT,TSBYT,(TCB) ; Get total (SEND) queued byte count
PKTIZ4:
; Force our idea of the availble window space to 0 if we cannot send
; data but have to generate only an ACK.
LOAD T1,TSSYN,(TCB) ; Send state
LOAD T2,TRSYN,(TCB) ; Receive state
CAIE T1,SYNABL ; If send side is SYNABL or
CAIN T2,SYNABL ; Recv side is SYNABL then
JRST PKTIZ5 ; Make useable window 0
; Compute the amount of window space available to send into as
; provided by the remote end.
LOAD T1,TSLFT,(TCB) ; Send Left
LOAD T3,TSWND,(TCB) ; Send Window offered
SKIPN T3 ; Allow sending if window is shut
MOVX T3,1 ; Need probe to sense remote window openning
LOAD T2,TSSEQ,(TCB) ; Send Sequence
ADD T1,T3 ; Compute Send Right
SUB T1,T2 ; Minus Sequence
MODSEQ T1 ; Keep within right number of bits
CAML T1,[MAXSEQ/2] ; If window space is .lt. 0 then
JRST PKTIZ5 ; Make it zero
MOVEM T1,WNDSPC ; Amount of useable window space
LOAD T2,TSMXP,(TCB) ; Max size of a packet (incl. header)
CAML T1,T2 ; If window is as large as a packet
JRST PKTIZ6 ; Go use it
LSH T1,2 ; 4*Useable
CAML T1,T3 ; If Useable/Offered .ge. 1/4
JRST PKTIZ6 ; Use it
JUMPE BFR,PKTIZ5 ; Cannot have PUSH if no buffer
JN BEOL,(BFR),PKTIZ6 ; Use anything if PUSH
PKTIZ5: SETZ WNDSPC, ; Make useable window 0
PKTIZ6:
SKIPLE BUFCNT ; If no data or
JUMPG WNDSPC,PKTIZ7 ; No useable window
JE TSFP,(TCB),PKTIZX ; Give up unless Force Pkt on.
PKTIZ7:
; Now the number of bytes available from the user buffer(s) is
; known and the apparent amount of useable window space is known.
; Set XFRCNT to the (maximum) amount which can actually be sent in this
; Pkt. In the case of a TVT, it is not known how much is available
; and we will assume a full packet (or window, etc) is to be sent.
CAML BUFCNT,WNDSPC ; Take min of what is available to be
SKIPA XFRCNT,WNDSPC ; sent and space allowed to send in
MOVE XFRCNT,BUFCNT
CAMLE XFRCNT,INTXPB ; Limit (roughly) to what a
MOVE XFRCNT,INTXPB ; Pkt can hold.
REPEAT 0,<
; Check to see if sequence numbers are being consumed at so high
; of a rate that the current packet may cross into the forbidden zone.
CALL GETISN ; Get initial sequence number
ADD T1,[SNSTEP] ; Plus a clock tick's worth of Seq nums.
MODSEQ T1
MOVEM T1,T2 ; Lower bound of forbidden region
LOAD T1,TSSEQ,(TCB) ; Current Send Sequence (to be Pkt Seq)
MOVE T3,T1
ADDI T3,5(XFRCNT) ; Allow for possible controls in Pkt
MODSEQ T3
CALL CHKWND ; Will Pkt cross into forbidden zone?
JUMPE T1,PKTIZ9 ; Jump if not.
JN TSFP,(TCB),PKTIZ8 ; Jump if we HAVE to emit a packet
MOVX T1,↑D1000 ; Wait for a second for more
CALL DLAYPZ ; sequence numbers to become available.
INCR TCTSQ,(TCB) ; Count them
JRST PKTIZX ; Background will cause PZ to run then.
PKTIZ8: MOVX XFRCNT,0 ; Don't send any data in Pkt.
PKTIZ9:
> ; End of REPEAT 0
; See if have a current packet to continue filling
LOAD PKT,TSCPK,(TCB) ; Partially filled packet?
JUMPE PKT,PKTZ10 ; No
XMOVEI TPKT,PKTELI(PKT) ; Locate TCP header
LOAD T1,PIDO,(PKT)
ADD TPKT,T1
LOAD T1,PTCKS,(TPKT) ; Total max packet size
LOAD T2,PIPL,(PKT) ; Current length
SUB T1,T2 ; Unused data space in pkt (NB in T1)
JRST PKTZ12
PKTZ10:
; Try to assign a block of free storage for the packet to be sent.
; MOVEI T1,(XFRCNT) ; Number of data bytes
LOAD T1,TSMXP,(TCB) ; Get a big packet
SETZ T2, ; Have a TCB
CALL TCPIPK ; Get packet & fill in headers
JRST [MOVX T1,↑D2000 ; Two seconds later,
CALL ENCPKT ; Try again.
INCR TCTBS,(TCB) ; Count them
JRST PKTIZX]
LOAD T2,PIPL,(PKT) ; Length of headers
ADD T2,T1 ; Max IP+TCP+Data length
STOR T2,PTCKS,(TPKT) ; Save it in case continue filling later
PUSH P,T1 ; T1 is max packet data count
; Enter the send sequence for the connection as the sequence number
; of the packet.
LOAD T1,TSSEQ,(TCB) ; Current send sequence
STOR T1,PSEQ,(TPKT) ; Packet sequence number
; Send a SYN if connection is opening
LOAD T1,TSSYN,(TCB) ; Get send state
CAIN T1,SYNABL ; SYNCHABLE means we must tell other
CALL SNDSYN ; end our seq. num. by sending a SYN
; PSYN to 1 & TSSYN to SYNSNT
POP P,T1 ; Max # data octets
; Transfer data from the user buffer (if any) to the packet. XFRCNT
; has the maximum number of bytes, which may be 0. T1 has unused bytes
; in packet. Find how much can really be sent, given options & header
PKTZ12: CAMLE XFRCNT,T1 ; Min against available data
MOVEI XFRCNT,(T1) ; Cannot send all the data in pkt
MOVE T1,XFRCNT ; # of octets for SNDxxx
; Call appropriate data transfer routine
JUMPE BFR,PKTZ14 ; Jump if no buffer from user SEND (or a TVT)
CALL SNDDAT ; Transfer it from user buffer to Pkt
JRST PKTZ15 ; Note that SNDDAT set timestamp
PKTZ14:
SKIPGE T2,LINBLK ; Do we have a terminal block?
JRST PKZ14A ; No.
CALL SNDTVT ; Send data from a virtual terminal
SETONE PEOL,(TPKT) ; Hussle up receiver
PKZ14A:
MOVE T2,TODCLK ; Current millisecond
STOR T2,PTS,(PKT) ; Set the Packet timestamp
PKTZ15: MOVEM T1,XFRCNT ; Save number actually sent
LOAD T1,PIPL,(PKT) ; Get packet length (b) witout data
ADD T1,XFRCNT ; Add amount just inserted
STOR T1,PIPL,(PKT) ; Set into Internet Packet Length
LOAD T1,TSBYT,(TCB) ; Reduce queued count too
SUB T1,XFRCNT
STOR T1,TSBYT,(TCB)
; Send a FIN if it is time. User must have said CLOSE (TSUOP
; bit is off), SEND connection must be synchronized, and there must be
; nothing waiting to be sent (no current send buffer and nothing Q'd)
LOAD T1,TSSYN,(TCB) ; Get send state
CAIE T1,SYNCED ; Connection synchronized?
JRST PKTZ16 ; No. No FIN can be sent.
JN TSUOP,(TCB),PKTZ16 ; Jump if connection still OPEN by user
JN TSCB,(TCB),PKTZ16 ; Still something to send. No FIN yet.
LOAD T1,QNEXT,<+TCBSBQ(TCB)> ; Get first thing on send bfr q
CAIN T1,TCBSBQ(TCB) ; Is the queue empty?
CALL SNDFIN ; Include a FIN in this packet
PKTZ16: ; PFIN is 1, TSSYN=FINSNT, DG scheduled
; if R=NOTSYN
; If we are ACKing a remote FIN, the receive side becomes NOTSYNCHED.
; If that makes both sides NOTSYNCHED, the user is notified that the
; connection is fully closed. If just the receive side closed, the
; user is told that the connection is closing. If all that remains
; to happen on the connection is that this end should receive an ACK
; of our FIN, Background is notified to generate a fake ACK after a
; reasonable time; this guards against the network losing the final ACK.
LOAD T1,TRSYN,(TCB) ; Get receive state
CAIE T1,FINRCV ; FINRECEIVED?
JRST PKTZ19 ; No. Skip the checks.
MOVX T1,NOTSYN
STOR T1,TRSYN,(TCB) ; Change to NOTSYNCHED
MOVX T1,XLP+↑D12 ; "Closing" code (Why not XFP+↑D12??)
CALL FLSRBF ; Flush receive buffers with this code
LOAD T2,TSSYN,(TCB) ; Get send state
CAIE T3,FINSNT ; Sending a FIN now?
JRST PKTZ17 ; No
XMOVEI T1,DG ; Who to signal
MOVE T2,TCPDGT ; When to signal
CALL SIGNAL
JRST PKTZ20 ; Force this packet out
PKTZ17:
MOVX T1,XFP+↑D12 ; Assume "Closing"
CAIN T2,NOTSYN ; Send side already closed?
MOVX T1,XLP+↑D3 ; Yes, "Closed" event
CALL USREVT ; Tell user
PKTZ19:
; Decide whether to wait for more data or send what packet now holds
JN TSFP,(TCB),PKTZ20 ; Send it if Force Packet is on
JN <PSYN,PEOL,PFIN>,(TPKT),PKTZ20 ; or packet contains control
LOAD T1,PIPL,(PKT) ; or if packet is full
LOAD T2,PTCKS,(TPKT)
CAMN T1,T2
JRST PKTZ20 ; Full, send it
STOR PKT,TSCPK,(TCB) ; Packet to be held
SETZRO <TSFP,TSEP>,(TCB) ; Clear signals
JRST PKTIZX
; Packet will be sent now, finish it off
PKTZ20: SETZRO TSCPK,(TCB) ; No saved packet
SETZRO <TSFP,TSEP>,(TCB) ; Clear signals
; Now all control and data have been stored in the packet. Advance
; the send sequence in the TCB to include all of this packet.
CALL PKTEND ; Returns next seq num after this Pkt
STOR T1,TSSEQ,(TCB) ; Advance Send sequence
STOR T1,PESEQ,(PKT) ; Save recomputed end of packet
JE PSYN,(TPKT),PKZ201 ; Jump if not first packet on this conn
LOAD T2,TSLFT,(TCB) ; Get send left
SUB T1,T2 ; Compute amount of window taken by this
MODSEQ T1 ; first Pkt.
STOR T1,TSWND,(TCB) ; And prevent further sends until window
PKZ201: ; info arrives from other end.
CALL NULPKT ; See if anything retransmittable here
SETCA T1, ; Get sense right
STOR T1,PPROG,(PKT) ; Say program must retain the packet
CALL SETRXP ; Setup packet rexmit parameters
; Set the timegenerated word in the local header. Used to compute
; roundtrip time for determining what the retransmit interval will be.
MOVE T1,TODCLK ; Current millisecond
STOR T1,PTG,(PKT) ; Packet Time Generated
; Done filling the packet. If running in secure mode and this
; packet has something which will be acknowledged, make the
; current level be the next level so as to shut off subsequent
; connection change request options to the KDC.
SKIPN INTSCR ; In secure mode?
JRST PKTZ21 ; No. Avoid the overhead.
JE PPROG,(PKT),PKTZ21 ; See if pkt will be ACK'd
LOAD T2,TSLVN,(TCB) ; Guaranteed that KDC will here the word
STOR T3,TSLVC,(TCB) ; So update the current level
PKTZ21:
LOAD T1,PPROG,(PKT) ; Save flag for later test
PUSH P,T1 ; PKT may vanish if error in EMTPKT (SNDGAT)
AOS PZPKCT ; Count Packetizer packets
MOVX T1,PT%XX1 ; "Being output"
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
MOVX T1,PT%TPZ
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
; Do statistics functions
SKIPN STATF ; Taking statistics right now?
JRST PKTZ22 ; No
MOVEI T1,OPDLAY ; Histogram time to (null) Output Proc.
CALL TSTAMP ; Process the time stamp
MOVEI T1,OPUSE ; Charge time to Output Processor
XMOVEI T2,EMTPKT ; for call to EmitPacket
MOVE T3,LINBLK ; TVT block if any
CALL TIMCAL ; Do a timed call.
JRST PKTZ23 ; Skip non-statistics code
PKTZ22: MOVE T1,LINBLK ; TVT block if any
CALL EMTPKT ; EmitPacket
PKTZ23:
POP P,T1 ; Saved PPROG tells if PKT needed RX
JUMPE T1,PKZ23A ; Not needed (maybe its been sent & freed)
SKIPN T1,PKT ; What to Enqueue (if there wasn't an error)
JRST PKZ23A ; Error in EMTPKT/SNDGAT
; (ought to do something, ICMP ??)
XMOVEI T2,TCBRXQ(TCB) ; Pointer to the retransmit queue
CALL NQ ; Enqueue it there
XMOVEI T1,RX ; Select the Retransmitter
LOAD T2,PRXI,(PKT) ; Retransmission interval
MOVE T4,T2
ADD T4,TODCLK ; Time of next run
SKIPE TCBQRX(TCB) ; Not queued, or ...
CAMG T4,TCBTRX(TCB) ; Need it sooner than scheduled?
CALL SIGNAL ; Cause RX to run after that time
PKZ23A:
; See if Packetizer should run again for this connection. This is true
; if there is something waiting to be sent and there is window space
; in which to send it.
; TVT checks
JE TTVT,(TCB),PKTZ24 ; Jump if not a virtual terminal
SETZ T1, ; Assume LINBLK is -1
SKIPLE T2,LINBLK ; Pointer to dynamic area
CALL TVTOSP ; Get amount of output waiting
STOR T1,TSBYT,(TCB)
JUMPE T1,PKTIZX ; None right now.
CAMLE WNDSPC,XFRCNT ; If there is still unused window space
JRST PKTIZ2 ; Go try for it
MOVE T1,TVTWTM ; After this number of milliseconds
CALL DLAYPZ ; Try again
JRST PKTIZX
PKTZ24:
; Non-TVT checks
JN TSCB,(TCB),PKTZ25 ; Jump if there is a current buffer
LOAD T1,QNEXT,<+TCBSBQ(TCB)> ; Send buffer queue
CAIN T1,TCBSBQ(TCB) ; Empty?
JRST PKTIZX ; Yes. Nothing to send. Return.
PKTZ25:
CAMLE WNDSPC,XFRCNT ; Is there some unused window space?
JRST PKTIZ1 ; Yes. Use it.
; Return from the Packetizer
PKTIZX: SKIPL T2,LINBLK ; Have a TVT line locked?
CALL ULKTTY ; Yes. Unlock it.
POP P,BFR
POP P,TPKT
POP P,PKT
RESTORE ; POP all locals
RET
; Set Initial Sequence Number for a connection.
; Sets default initial send window.
;TCB/ (Extended) Locked connection block
;
; CALL SETISN
;Ret+1: always
SETISN: JN TSSV,(TCB),SETIS1 ; Jump if the current sequence is valid
CALL GETISN ; Get current value of ISN curve
STOR T1,TSSEQ,(TCB) ; Store as current send sequence
SETONE TSSV,(TCB) ; Indicate sequence is now valid
SETIS1:
LOAD T1,TSSEQ,(TCB)
STOR T1,TSLFT,(TCB) ; Move Send Left up to Sequence
RET ; Wait for window
MOVE T1,INTXPB ; Maximum data in one packet
SUBI T1,MINIHS+MINTHS ; without options for biggest net
STOR T1,TSWND,(TCB) ; is the default initial window
RET ; (May be reduced when SYN sent)
;Include a SYN bit in the packet
;TCB/ (Extended) Current locked connection block
;PKT/ (Extended) Packet
;TPKT/ (Extended) pointer to TCP part of packet
;
; CALL SNDSYN
;Ret+1: always
SNDSYN: SETONE PSYN,(TPKT) ; Set the SYN bit in the packet
LOAD T1,TSSYN,(TCB) ; Get send state
CAIE T1,SYNABLE ; SYNCABLE (ie, opening)
JRST SNDSY1 ; No.
MOVX T2,SYNSNT
STOR T2,TSSYN,(TCB) ; Yes. Change to SYNSENT state.
SNDSY1: AOS SYNSCT ; Count SYNs sent
RET
; Move data from user buffer(s) to a (partially filled) packet.
; All OPTIONS must be in the Packet at the time this is called.
;TCB/ (Extended) Locked connection block
;PKT/ (Extended) Packet
;TPKT/ (Extended) pointer to TCP part of packet
;BFR/ (Extended) Buffer header address (of first buffer)
;T1/ Number of bytes to move (maybe 0).
;
; CALL SNDDAT
;Ret+1: Always. T1 has number actually transferred
SNDDAT: LOCAL <CPYCNT,XFRCNT,PKTPTR>
MOVEM T1,XFRCNT ; Set up the transfer count
SETZ CPYCNT,
LOAD T1,PIPL,(PKT) ; Packet length
IDIVI T1,4 ; Divided into
MOVEI PKTPTR,PKTELI(T1) ; Word offset and byte
HLL PKTPTR,[ POINT 8,.-.(PKT)
POINT 8,.-.(PKT),7
POINT 8,.-.(PKT),15
POINT 8,.-.(PKT),23](T2)
SKIPN STATF ; Taking statistics now?
JRST SNDDA0 ; No
LOAD T1,BTS,(BFR) ; Get the buffer time stamp
STOR T1,PTS,(PKT) ; and make that the Packet time stamp
SNDDA0:
; Top of per-user buffer loop
SNDDA1: CALL SETTUM ; Set user map
LOAD T4,BCNT,(BFR) ; Available bytes in user buffer
MOVE T3,T4
CAILE T3,(XFRCNT) ; Min'ed with remaining space in packet
MOVE T3,XFRCNT
SUB XFRCNT,T3 ; Bytes to transfer from next buffer
SUB T4,T3 ; Bytes remaining in user buffer
STOR T4,BCNT,(BFR)
ADDM T3,CPYCNT ; Total bytes transferred so far
LOAD T1,BPTR,(BFR) ; Source is buffer ptr (mapped into mon)
MOVE T2,PKTPTR ; Destination is packet pointer
SETO T4, ; Indicate User-to-monitor
CALL XFRDAT ; Do the data transfer
MOVEM T2,PKTPTR ; Store updated infomation
STOR T1,BPTR,(BFR)
CALL USTTUM ; Unmap user space
; Stop if more remains in user buffer (should have reached end of count
JN BCNT,(BFR),SNDDAX ; More remains in buffer, wait for next pkt
; Finished with this buffer, transfer PUSH if present
SETZRO TSCB,(TCB) ; Done with this buffer
JE BEOL,(BFR),SNDDA5 ; Jump if no PUSH in the buffer
SETONE PEOL,(TPKT) ; Set packet PUSH
SNDDA5:
REPEAT 0,<
MOVEI T1,PZDLAY ; Select the packetizer delay histogram
SKIPE STATF ; Taking statistics now?
CALL TSTAMP ; Yes. Process the timestamp
>
MOVX T1,<<OK>B7> ; The general success event code
CALL USRBFE ; Tell user his send buffer is empty
MOVX T1,PT%TBD
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
; Stop if transferred full count
JUMPLE XFRCNT,SNDDAX ; Pkt filled so quit
; Setup for next user buffer
LOAD T1,QNEXT,<+TCBSBQ(TCB)> ; Get next thing on send buf Q
CAIN T1,TCBSBQ(TCB) ; Next points at header ...
JRST SNDDAX ; means empty. Something is wrong...stop
SETSEC T1,INTSEC ; Make extended address
CALL DQ ; Dequeue the buffer
MOVE BFR,T1 ; And setup the standard pointer.
STOR BFR,TSCB,(TCB) ; Remember as current buffer
JRST SNDDA1 ; Go to work on this buffer
SNDDAX: ADDM CPYCNT,BYTSCT ; Count Bytes Sent
MOVE T1,CPYCNT ; Return bytes actually transferred
RESTORE
RET
; Send a FIN in this packet
;TCB/ (Extended) Locked connection block
;PKT/ (Extended) Packet
;TPKT/ (Extended) pointer to TCP part of packet
;
; CALL SNDFIN
;Ret+1: always
SNDFIN: SETONE PFIN,(TPKT) ; Set FIN bit in the packet
MOVX T1,FINSNT ; New send state
STOR T1,TSSYN,(TCB) ; Set it.
LOAD T1,TRSYN,(TCB) ; Get receive state
CAIE T1,NOTSYN ; NOTSYNCHED?
JRST SNDFI1 ; No.
XMOVEI T1,DG
MOVE T2,TCPDGT
CALL SIGNAL ; Run again in 30 seconds
SNDFI1:
AOS FINSCT ; Count FINs sent
RET
; Emit a packet into a network
;TCB/ (Extended) Locked connection block
;PKT/ (Extended) Packet, NB: It may be invalid on return from SNDGAT
;TPKT/ (Extended) pointer to TCP part of packet
;T1/ Pointer to dynamic data if TVT w/ output data, or -1
EMTPKT::LOCAL <LINBLK>
MOVEM T1,LINBLK ; Save TTYSRV line block if any
JN PINTL,(PKT),EMTPKX ; Already in use by interrupt level
; (ReXmit while NET is off or slow)
LOAD T4,TRSYN,(TCB) ; Get receive state
CAIN T4,SYNABLE ; SYNCABLE
JRST EMTPK1 ; Yes. Cannot ACK anything.
; Insert ACK
SETONE PACK,(TPKT) ; Set the packet ACK bit
LOAD T1,TRLFT,(TCB) ; Receive Left is what we want to hear
STOR T1,PACKS,(TPKT) ; next. ACK that.
STOR T1,TRLAK,(TCB) ; Sending an ACK
; Insert receive window
CAIE T4,FINRCV ; If received FIN or
CAIN T4,NOTSYN ; already closed
JRST EMTPK0 ; Leave window zero
LOAD T1,TRWND,(TCB) ; Offered window size
STOR T1,PWNDO,(TPKT) ; into packet
JE PACK,(TPKT),EMTPK0 ; If no ACK, cannot
LOAD T2,PACKS,(TPKT) ; Receive-left
ADD T2,T1 ; Plus window
MODSEQ T2 ; is Receive-right
STOR T2,TRLWN,(TCB) ; Last offered
EMTPK0:
; Set urgent if required
JE TSURG,(TCB),EMTPK1 ; Skip following if not in urgent mode
LOAD T1,TSURP,(TCB) ; End of urgent data
LOAD T2,PSEQ,(TPKT) ; Sequence number of this packet
SUB T1,T2 ; Offset to urgent pointer
CAIG T1,<PURGP/<PURGP&<-PURGP>>> ; Limit to max
MOVX T1,<PURGP/<PURGP&<-PURGP>>>
STOR T1,PURGP,(TPKT) ; Set into packet
SETONE PURG,(TPKT) ; Set the control bit
EMTPK1:
REPEAT 0,<
SKIPN STATF ; Taking statistics right now?
JRST EMTPK2 ; No.
LOAD T1,PTS,(PKT) ; Get the current timestamp
CALL SETTSO ; and move into the timestamp option
EMTPK2:
>
; Insert checksum
SETZRO PTCKS,(TPKT) ; Clear the check sum field
CALL TCPCKS ; Compute the packet checksum
STOR T1,PTCKS,(TPKT) ; and enter it in the packet
; Log packet
MOVX T1,PT%XX2 ; "passing Output processor" code
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes, Tell Pkt printer
MOVX T1,PT%TRX
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
MOVE T1,TODCLK ; Current millisecond number
STOR T1,PXT,(PKT) ; Store as time of most recent transmit
CALL SNDGAT ; Send it
AOS OPPKCT ; Count packets output
AOS OPRNCT ; and Output runs
EMTPKX: RESTORE
RET
; Send a RST to remote TCP when this end is abandonned.
; This is a courtesy which serves to speed things up but is not
; required by the protocol.
;TCB/ (Extended) Locked connection block
;
; CALL ABTNTC
;Ret+1: always
ABTNTC: PUSH P,PKT
PUSH P,TPKT
SETZB T1,T2 ; No data & have TCB
CALL TCPIPK ; Get packet & fill in headers
JRST ABTNTX ; No space. Not required anyway.
SETONE PRST,(TPKT) ; Set the reset bit
LOAD T1,TSSEQ,(TCB) ; Current send sequence
STOR T1,PSEQ,(TPKT) ; Is the Packet sequence
LOAD T2,TRLFT,(TCB) ; What we want to hear next (or 0)
STOR T2,PACKS,(TPKT) ; Is the acknowledge
SETONE PACK,(TPKT) ; Make the ACK Sequence meaningful
SETZRO PWNDO,(TPKT) ; 0 window to other end (TCB is dead)
SETZRO PTCKS,(TPKT)
CALL TCPCKS ; Compute TCP packet checksum
STOR T1,PTCKS,(TPKT) ; and insert into packet
MOVX T1,PT%XX2 ; Say Pkt is being sent
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes, Print the packet
MOVX T1,PT%TRX
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
AOS PZPKCT ; Count Packetized packets
AOS OPPKCT ; Count Output packets
AOS RSTSCT ; Count ERRs sent
CALL SNDGAT ; Sent Pkt to the net. (NB: PPROG is 0)
ABTNTX: POP P,TPKT
POP P,PKT
RET
; Send a "Secure Close"
; After all has been said, one more packet containing a SCLOPT must
; be sent. This will cause the BCR to contact the KDC in order
; to remove the keys, etc for the connection.
;TCB/ (Extended) Locked connection block
;
; CALL SCRCLS
;Ret+1: always
SCRCLS::PUSH P,PKT
PUSH P,TPKT
SETZB T1,T2 ; No data & have TCB
CALL TCPIPK ; Get packet & fill in headers
JRST SCRCLX ; No. Don't worry about it however
;?? need help here
CALL SNDSCL ; Add in the secure close option
CALL TCPCKS ; Compute TCP packet checksum
STOR T1,PTCKS,(PKT) ; and insert into packet
MOVX T1,PT%XX2 ; Say Pkt is being sent
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes, Print the packet
MOVX T1,PT%TRX
TDNE T1,INTTRC ; Want trace?
CALL PRNPKT ; Yes
AOS PZPKCT ; Count Packetized packets
AOS OPPKCT ; Count Output packets
CALL SNDGAT ; Sent Pkt to the net. (NB: PPROG is 0)
SCRCLX: POP P,TPKT
POP P,PKT
RET
; Force a packet
; Causes a packet to be emitted even if there is no data to send.
; Done to cause something to be ACKd for instance.
;TCB/ (Extended) Locked connection block
;
; CALL FRCPKT
;Ret+1: always
FRCPKT::JN TSFP,(TCB),FRCPKX ; Filter extra calls
SETONE TSFP,(TCB) ; Set the Force packet request bit
$SIGNL(PZ,0) ; Make Packetizer run now
FRCPKX: RET
; Encourage generation of a packet
; A packet is needed to ACK something, but allow time for some data
; to appear so that the ACK can piggyback on it. Also, more calls
; may be made and we wish to minimize network traffic by not
; generating an ACK-only packet each time.
;TCB/ (Extended) Locked connection block
;T1/ # msec to wait
;
; CALL ENCPKT
;Ret+1: always
ENCPKT::JN TSEP,(TCB),ENCPKX ; Already encouraged. No more needed.
SETONE TSEP,(TCB) ; Remember a packet is being encouraged
JFCL ;MOVX T1,↑D100 ; The wait time in milliseconds
CALL DLAYPZ ; Get background to signal PZ(TCB)
ENCPKX: RET
; Schedule a delayed signal for the packetizer
;TCB/ (Extended) Locked TCB
;T1/ Delay time in milliseconds
;
; CALL DLAYPZ
;Ret+1: always
DLAYPZ::JN TSFP,(TCB),DLAYPX ; Already forced. No need.
MOVE T2,T1 ; Desired delay for SIGNAL
SKIPN TCBQPZ(TCB) ; Already queued?
JRST DLAYPS ; No
MOVE T1,TCBTPZ(TCB) ; When
SUB T1,TODCLK
CAMG T1,T2 ; This request sooner?
JRST DLAYPX ; No, its later so ignore it
DLAYPS: XMOVEI T1,PZ ; Select Packetizer
CALL SIGNAL
DLAYPX: RET
; Flush all SEND buffers with a given Event Code
;TCB/ (Extended) Locked connection block
;T1/ Event Code: EFP+↑D7; ELP+↑D7; ELP+↑D14, ELT+↑D4 (no TVTs)
;
; CALL FLSSBF
;Ret+1: always
FLSSBF::LOCAL <CODE>
PUSH P,BFR
MOVEM T1,CODE
LOAD BFR,TSCB,(TCB) ; Get the current send buffer
SETZRO TSCB,(TCB) ; Forget the current send buffer
JUMPE BFR,FLSSB2 ; Do we have a buffer here?
SETSEC BFR,INTSEC ; Make extended address
FLSSB1: MOVE T1,CODE ; Yes.
LSH T1,↑D<36-8> ; Position in error byte
CALL USRBFE ; Tell user it is done
FLSSB2: LOAD BFR,QNEXT,<+TCBSBQ(TCB)> ; Get next thing on buffer queue
CAIN BFR,TCBSBQ(TCB) ; Points at head of queue
JRST FLSSBX ; means empty queue. Done.
SETSEC BFR,INTSEC ; Make extended address
MOVE T1,BFR
CALL DQ ; Dequeue the buffer
JRST FLSSB1
FLSSBX: POP P,BFR
RESTORE
RET
; PZINI Initialize PZ process block
; CALL PZINI
;Ret+1: ALways
PZINI:: LOCAL <PRC>
MOVEI PRC,PZ ; Pointer to the Process block for PZ
MOVX T1,QSZ ; Size of a queue head
CALL GETBLK ; Head must be in same section as items
JUMPE T1,PZINIX ; No room
MOVEM T1,PRCQ(PRC) ; Input queue
CALL INITQ ; Initialize it
XMOVEI T1,PRCLCK(PRC) ; Lock
CALL CLRLCK ; Initilize it
XMOVEI T1,PKTIZE ; Packetizer function
MOVEM T1,PRCROU(PRC) ; Routine address
SETOM PRCWAK(PRC) ; No run time yet
MOVE T1,[<GIW TCBQPZ,TCB>]; Offset of PZ queue in TCB
MOVEM T1,PRCQOF(PRC) ; Store process block
MOVE T1,[<GIW TCBTPZ,TCB>]; Offset of PZ run time in TCB
MOVEM T1,PRCWOF(PRC) ; Store in process block
HRLOI T1,377777 ; Infinity
MOVEM T1,PRCSGT(PRC) ; Set time of most recent signal
MOVEI T1,PZRNCT ; Pointer to run counter
MOVEM T1,PRCRNC(PRC) ; Put in standard place
MOVEI T1,PZUSE ; Pointer to CPU use meter
MOVEM T1,PRCTMR(PRC) ; Put in standard place
HRROI T1,-1 ; All ok
PZINIX: RESTORE
RET
TNXEND
END